/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "StorageClient.h"
#include "protect_engines/kubernetes/common/KubeErrorCodes.h"
#include "common/Constants.h"
#include "common/utils/Utils.h"
#include "curl_http/HttpStatus.h"
#include "json/json.h"
#include "thrift_interface/ApplicationProtectPlugin_types.h"
#include "config_reader/ConfigIniReader.h"

using namespace VirtPlugin;
using  Module::HttpRequest;
using  Module::IHttpResponse;
using  Module::IHttpClient;

namespace {
const std::string MODULE_NAME = "KubeClient";
const std::string METHOD_POST = "POST";
const std::string METHOD_GET = "GET";
const std::string METHOD_PUT = "PUT";
const std::string METHOD_DELETE = "DELETE";
const uint32_t SEND_HTTP_DELAY_TIME = 3;  // 3s delay
const uint32_t HTTP_TIME_OUT = 30;
const uint32_t SEND_HTTP_MAX_RETRY_TIMES = 3;
const std::set<std::string> OceanStor_V5_PRODUCT_MODE = {"92", "93", "94", "95", "96", "97", "98", "99", "100", "101",
                                                         "102", "103", "104", "105", "106", "107", "115", "116", "117",
                                                         "118", "119", "120", "121", "122", "123", "124", "125", "126",
                                                         "127", "128", "129", "130", "131", "132", "133", "134", "135",
                                                         "137"};
const std::set<std::string> OceanStor_V3_PRODUCT_MODE = {"61", "62", "63", "64", "68", "69", "70", "71", "72", "73",
                                                         "74", "82", "84", "85", "86", "87", "88", "89", "90", "108",
                                                         "112", "113", "114"};
const std::set<std::string> Dorado_V6_PRODUCT_MODE = {"811", "812", "813", "814", "815", "816", "817", "818", "819",
                                                      "821", "822", "823", "824", "825", "826", "827", "828", "829",
                                                      "830", "831", "832", "833", "834", "835", "836", "837", "838",
                                                      "839", "840"};
const std::string OCEANSTOR_V5 = "V5";
const std::string DORADO_V6 = "V6";
const std::string OTHER_DEVICE_VERSION = "Others";
// 500、502、503、504、0为网络错误
const std::vector<int> NETWORK_HTTP_STATUS = {Module::SC_BAD_GATEWAY, Module::SC_SERVICE_UNAVAILABLE,
    Module::SC_GATEWAY_TIMEOUT, Module::SC_INTERNAL_SERVER_ERROR, 0};

const std::vector<int> WRONG_IP_STATUS = {Module::SC_BAD_GATEWAY, Module::SC_SERVICE_UNAVAILABLE,
    Module::SC_GATEWAY_TIMEOUT, Module::SC_INTERNAL_SERVER_ERROR, -1};

const int32_t NO_USER_PERMISSION = 1077949058; // 用户权限不足
const int32_t AUTH_IP_INCONSISTENCY = 1073793620; // 当前发起请求的IP地址与登录时的IP地址不一致
const int32_t UN_AUTH = -401;
const std::vector<int> NO_AUTH_HTTP_STATUS = {NO_USER_PERMISSION, AUTH_IP_INCONSISTENCY, UN_AUTH};
const std::string SNAPSHOTTYPE = "27";
}

namespace KubernetesPlugin {
std::mutex StorageClient::CreateClientMutex; // 用于客户端的互斥创建
std::mutex StorageClient::ReCheckAccessAuthMutex; // 用于重新登录认证互斥访问

StorageClient::~StorageClient() {}

std::pair <int32_t, std::shared_ptr<StorageClient>> StorageClient::Create(const std::string &ip, int port,
    const AccessAuthParam &accessAuthParam, const std::string &ipList, bool isRetry)
{
    std::lock_guard <std::mutex> lock(CreateClientMutex);
    std::vector<std::string> ipVector;
    Utils::GetIpVectorFromString(ipVector, ip, ipList);
    INFOLOG("ipVector size:%d, front:%s, back:%s", ipVector.size(), ipVector.front().c_str(), ipVector.back().c_str());
    std::shared_ptr <StorageClient> storageClient;
    for (const std::string &authIp : ipVector) {
        INFOLOG("authIp:%s", authIp.c_str());
        auto it = StorageClientMap.find(authIp + std::to_string(port) + accessAuthParam.m_userName +
            accessAuthParam.m_userkey);
        if (it != StorageClientMap.end() && isRetry) {
            DBGLOG("Storage client map contains this ip address, ip:%s, port:%s", ip.c_str(),
                std::to_string(port).c_str());
            return std::make_pair(SUCCESS, it->second);
        }
        auto [ret, storageDeviceAuthResponse] = CheckAccessAuthentication(authIp, port, accessAuthParam, isRetry);
        if (storageDeviceAuthResponse.error.code != 0) {
            // 根据设备反馈错误码，抛出
            ret = storageDeviceAuthResponse.error.code;
            ERRLOG("Storage device authentication failed, ip: %s, port: %d, username: %s, error code: %d",
                ip.c_str(), port, accessAuthParam.m_userName.c_str(), ret);
            if (!isRetry) {
                return std::make_pair(ret, storageClient);
            }
        } else {
            storageClient = std::make_shared<StorageClient>(authIp, port, accessAuthParam,
                                                            storageDeviceAuthResponse.storageDeviceAuthData, ipVector);
            StorageClientMap[authIp + std::to_string(port) + accessAuthParam.m_userName +
                            accessAuthParam.m_userkey] = storageClient;
            if (!isRetry) {
                continue;
            }
            return std::make_pair(ret, storageClient);
        }
    }
    INFOLOG("Create finish check all.");
    return isRetry ? std::make_pair(FAILED, storageClient) : std::make_pair(SUCCESS, storageClient);
}

void StorageClient::CheckIpValidty(const std::string &ip, int port, const std::string &ipList,
    const AccessAuthParam &accessAuthParam, std::string &errorStr)
{
    std::vector<std::string> ipVector;
    Utils::GetIpVectorFromString(ipVector, ip, ipList);
    for (const std::string &authIp : ipVector) {
        auto it = StorageClientMap.find(authIp + std::to_string(port) + accessAuthParam.m_userName +
            accessAuthParam.m_userkey);
        if (it == StorageClientMap.end()) {
            DBGLOG("Storage client map contains this ip address, ip:%s, port:%s", ip.c_str(),
                std::to_string(port).c_str());
            errorStr = authIp;
            return;
        }
    }
}

int32_t StorageClient::Logout()
{
    auto[ret, resBody]= SendRequest(METHOD_DELETE,
                                    "/deviceManager/rest/" + storageDeviceAuthData.deviceId + "/sessions",
                                    "");
    StorageCommonResponse storageCommonResponse;
    if (!Module::JsonHelper::JsonStringToStruct(resBody, storageCommonResponse)) {
        ERRLOG("Fail to parse storage response to struct, response: %s", resBody.c_str());
        ret = FAILED;
    }
    if (storageCommonResponse.m_error.code != 0) {
        // 根据设备反馈错误码，抛出
        ERRLOG("Fail to clean session, ip: %s, error code: %d, error message: %s", m_ip.c_str(),
            storageCommonResponse.m_error.code, storageCommonResponse.m_error.description.c_str());
        ret = storageCommonResponse.m_error.code;
    }
    return ret;
}

std::pair<int32_t, SnapshotInfoData> StorageClient::QuerySnapshot(const std::string &snapshotId)
{
    auto[ret, resBody] = SendRequest(METHOD_GET,
                                     "/deviceManager/rest/" + storageDeviceAuthData.deviceId + "/snapshot/" +
                                     snapshotId, "");
    SnapshotCreateResponse snapshotCreateResponse;
    if (!Module::JsonHelper::JsonStringToStruct(resBody, snapshotCreateResponse)) {
        ERRLOG("Fail to parse snapshot query response to struct, response: %s", resBody.c_str());
        ret = FAILED;
    }
    if (snapshotCreateResponse.m_error.code != 0) {
        ERRLOG("Fail to query snapshot, ip: %s, snapshot ID: %s, error code: %d, error message: %s", m_ip.c_str(),
            snapshotId.c_str(), snapshotCreateResponse.m_error.code,
            snapshotCreateResponse.m_error.description.c_str());
        ret = snapshotCreateResponse.m_error.code;
    }
    return std::make_pair(ret, snapshotCreateResponse.m_snapshotInfoData);
}

std::pair<int32_t, SnapshotInfoResponse> StorageClient::GetLunSnapshots(const std::string &lunName)
{
    std::string uri = "/deviceManager/rest/" + storageDeviceAuthData.deviceId +
                      "/snapshot?range=%5B0-100%5D&filter=SUBTYPE%3A%3A0%20and%20PARENTNAME%3A%3A" + lunName;
    auto[ret, resBody] = SendRequest(METHOD_GET, uri, "");
    SnapshotInfoResponse snapshotInfoResponse;
    Module::JsonHelper::JsonStringToStruct(resBody, snapshotInfoResponse);
    if (snapshotInfoResponse.m_error.code != 0) {
        ERRLOG("Fail to get lun snapshot info, ip: %s, lun name: %s, error code: %d, error message: %s",
            m_ip.c_str(), lunName.c_str(), snapshotInfoResponse.m_error.code,
            snapshotInfoResponse.m_error.description.c_str());
        ret = snapshotInfoResponse.m_error.code;
    }
    return std::make_pair(ret, snapshotInfoResponse);
}

std::pair<int32_t, StorageDeviceInfo> StorageClient::GetDeviceBaseInfo()
{
    auto[ret, resBody] = SendRequest(METHOD_GET, "/deviceManager/rest/" + storageDeviceAuthData.deviceId + "/system/",
                                     "");
    StorageDeviceBaseInfoResponse storageDeviceBaseInfoResponse;
    Module::JsonHelper::JsonStringToStruct(resBody, storageDeviceBaseInfoResponse);
    if (storageDeviceBaseInfoResponse.error.code != 0) {
        // 根据设备反馈错误码，抛出
        ERRLOG("Fail to get storage base info, ip: %s, error code: %d, error message: %s", m_ip.c_str(),
            storageDeviceBaseInfoResponse.error.code, storageDeviceBaseInfoResponse.error.description.c_str());
        ret = storageDeviceBaseInfoResponse.error.code;
    }
    StorageDeviceInfo storageDeviceInfo;
    storageDeviceInfo.sn = storageDeviceBaseInfoResponse.baseInfoData.wwn;
    storageDeviceInfo.productMode = storageDeviceBaseInfoResponse.baseInfoData.productMode;
    storageDeviceInfo.productVersion = storageDeviceBaseInfoResponse.baseInfoData.productVersion;
    storageDeviceInfo.name = storageDeviceBaseInfoResponse.baseInfoData.name;
    if (OceanStor_V5_PRODUCT_MODE.find(storageDeviceInfo.productMode) != OceanStor_V5_PRODUCT_MODE.end()) {
        storageDeviceInfo.version = OCEANSTOR_V5;
    } else if (Dorado_V6_PRODUCT_MODE.find(storageDeviceInfo.productMode) != Dorado_V6_PRODUCT_MODE.end()) {
        storageDeviceInfo.version = DORADO_V6;
    } else {
        storageDeviceInfo.version = OTHER_DEVICE_VERSION;
    }
    return std::make_pair(ret, storageDeviceInfo);
}

std::pair<int32_t, StorageDeviceAuthResponse> StorageClient::CheckAccessAuthentication(const std::string &ip, int port,
    const AccessAuthParam &accessAuth, bool isRetry)
{
    Json::Value body;
    body["username"] = accessAuth.m_userName;
    body["password"] = accessAuth.m_userkey;
    body["scope"] = accessAuth.m_scope;

    Json::FastWriter writer;
    std::string bodyString = writer.write(body);

    HttpRequest httpReqest;
    InitHttpRequest(httpReqest);
    httpReqest.method = METHOD_POST;
    httpReqest.url = "https://" + ip + ":" + std::to_string(port) + "/deviceManager/rest/xxxxx/sessions";
    httpReqest.body = std::move(bodyString);
    httpReqest.isVerify = Module::CACertVerification::DO_NOT_VERIFY;

    HttpResponseInfo httpResponse;
    int ret = Send(httpReqest, httpResponse, isRetry);

    StorageDeviceAuthResponse storageDeviceAuthResponse;
    Module::JsonHelper::JsonStringToStruct(httpResponse.m_body, storageDeviceAuthResponse);
    storageDeviceAuthResponse.storageDeviceAuthData.cookies = httpResponse.cookies;
    return std::make_pair(ret, storageDeviceAuthResponse);
}

std::pair<int32_t, LunInfoData> StorageClient::GetLunInfoData(const std::string &lunName)
{
    std::string uri = "/deviceManager/rest/" + storageDeviceAuthData.deviceId +
                      "/lun?range=%5B0-100%5D&filter=SUBTYPE%3A%3A0%20and%20NAME%3A" + lunName;
    auto[ret, resBody] = SendRequest(METHOD_GET, uri, "");
    LunInfoResponse lunInfoResponse;
    Module::JsonHelper::JsonStringToStruct(resBody, lunInfoResponse);
    if (lunInfoResponse.m_error.code != 0) {
        // 根据设备反馈错误码，抛出
        ERRLOG("Fail to get LUN info, ip: %s, lun name: %s, error code: %d, error message: %s",
            m_ip.c_str(), lunName.c_str(), lunInfoResponse.m_error.code,
            lunInfoResponse.m_error.description.c_str());
        ret = lunInfoResponse.m_error.code;
    }

    if (ret == SUCCESS && lunInfoResponse.m_lunInfoDatas.empty()) {
        ERRLOG("Fail to get LUN info, ip: %s, There is not any lun (name: %s)", m_ip.c_str(), lunName.c_str());
        ret = OPERATION_ERROR;
    }

    LunInfoData lunInfoData;
    for (const auto &item : lunInfoResponse.m_lunInfoDatas) {
        if (item.m_name == lunName) {
            lunInfoData = item;
            break;
        }
    }
    return std::make_pair(ret, lunInfoData);
}

std::pair<int32_t, SnapshotInfoData> StorageClient::CreateSnapshot(SnapshotCreateParam &snapshotCreateParam)
{
    std::string reqBody;
    Module::JsonHelper::StructToJsonString(snapshotCreateParam, reqBody);

    std::string uri = "/deviceManager/rest/" + storageDeviceAuthData.deviceId + "/snapshot";
    auto[ret, resBody] = SendRequest(METHOD_POST, uri, reqBody);
    SnapshotCreateResponse snapshotCreateResponse;
    Module::JsonHelper::JsonStringToStruct(resBody, snapshotCreateResponse);
    if (snapshotCreateResponse.m_error.code != 0) {
        // 根据设备反馈错误码，抛出
        ERRLOG("Fail to create snapshot, ip: %s, LUN ID: %s, error code: %d, error message: %s", m_ip.c_str(),
            snapshotCreateParam.m_name.c_str(), snapshotCreateResponse.m_error.code,
            snapshotCreateResponse.m_error.description.c_str());
        ret = snapshotCreateResponse.m_error.code;
    }
    return std::make_pair(ret, snapshotCreateResponse.m_snapshotInfoData);
}

int32_t StorageClient::StopSnapshot(const std::string &snapshotId)
{
    Json::Value reqBodyJson;
    reqBodyJson["ID"] = snapshotId;
    Json::FastWriter writer;
    std::string reqBody = writer.write(reqBodyJson);

    std::string uri = "/deviceManager/rest/" + storageDeviceAuthData.deviceId + "/snapshot/stop";
    auto[ret, resBody] = SendRequest(METHOD_PUT, uri, reqBody);
    StorageCommonResponse storageCommonResponse;
    Module::JsonHelper::JsonStringToStruct(resBody, storageCommonResponse);
    if (storageCommonResponse.m_error.code != 0) {
        // 根据设备反馈错误码，抛出
        ERRLOG("Fail to stop snapshot, ip: %s, snapshot ID: %d, error code: %d, error message: %s", m_ip.c_str(),
            snapshotId, storageCommonResponse.m_error.code,
            storageCommonResponse.m_error.description.c_str());
        ret = storageCommonResponse.m_error.code;
    }
    return ret;
}

int32_t StorageClient::RemoveSnapshotFromLunGroupExp(const std::string &snapshotId)
{
    std::vector<LunGroupInfo> LunGroupList;
    if (QueryAssociateLunGroup(snapshotId, SNAPSHOTTYPE, LunGroupList) != SUCCESS) {
        ERRLOG("Fail to query snapshot LunGroup snapshot id: %s", snapshotId.c_str());
        return FAILED;
    }
    if (LunGroupList.empty()) {
        DBGLOG("No need to remove LG, snapshot not associate with LG, id: %s", snapshotId.c_str());
        return SUCCESS;
    }
    if (RemoveObjFromLunGroup(snapshotId, SNAPSHOTTYPE, LunGroupList) != SUCCESS) {
        ERRLOG("Remove snapshot from lun group failed! Snapshot id: %s", snapshotId.c_str());
        return FAILED;
    }
    return SUCCESS;
}

int32_t StorageClient::QueryAssociateLunGroup(const std::string &objId, const std::string &objType,
    std::vector<LunGroupInfo> &LunGroupList)
{
    std::string uri = "/deviceManager/rest/" + storageDeviceAuthData.deviceId +
        "/lungroup/associate?ASSOCIATEOBJTYPE=" + objType + "&ASSOCIATEOBJID=" + objId;
    auto[ret, resBody] = SendRequest(METHOD_GET, uri, "");
    LunGroupInfoResponse lunGroupInfoResponse;
    Module::JsonHelper::JsonStringToStruct(resBody, lunGroupInfoResponse);
    if (lunGroupInfoResponse.error.code != 0) {
        // 根据设备反馈错误码，抛出
        ERRLOG("Fail to query snapshot lungroup, ip: %s, error code: %d, error message: %s",
            m_ip.c_str(), lunGroupInfoResponse.error.code, lunGroupInfoResponse.error.description.c_str());
        ret = lunGroupInfoResponse.error.code;
        return ret;
    }
    LunGroupList = lunGroupInfoResponse.data;
    return lunGroupInfoResponse.error.code;
}

int32_t StorageClient::RemoveObjFromLunGroup(const std::string &objId, const std::string &objType,
    std::vector<LunGroupInfo> &LunGroupList)
{
    std::string uri;
    std::pair <int32_t, std::string> resPair;
    int iret = SUCCESS;
    for (const auto lunG : LunGroupList) {
        uri = "/deviceManager/rest/" + storageDeviceAuthData.deviceId + "/lungroup/associate?ID=" +
        lunG.id + "&ASSOCIATEOBJTYPE=" + objType + "&ASSOCIATEOBJID=" + objId;
        resPair = SendRequest(METHOD_DELETE, uri, "");
        StorageCommonResponse storageCommonResponse;
        Module::JsonHelper::JsonStringToStruct(resPair.second, storageCommonResponse);
        if (storageCommonResponse.m_error.code != 0) {
            // 根据设备反馈错误码，抛出
            ERRLOG("Fail to Remove Obj (%s, type: %s) from lungroup (%s), ip: %s, error code: %d, error message: %s",
                objId.c_str(), objType.c_str(), lunG.id.c_str(), m_ip.c_str(), storageCommonResponse.m_error.code,
                storageCommonResponse.m_error.description.c_str());
            iret = storageCommonResponse.m_error.code;
            continue;
        }
    }
    return iret;
}

int32_t StorageClient::QueryStoragePool(const std::string &poolId, StoragePoolData &poolData)
{
    if (poolId.empty()) {
        ERRLOG("Fail to query storage pool, ip: %s, error message: The input parameter is invalid, pool id is empty",
            m_ip.c_str());
        return FAILED;
    }
    std::string uri = "/deviceManager/rest/" + storageDeviceAuthData.deviceId +
        "/storagepool/" + poolId;
    auto[ret, resBody] = SendRequest(METHOD_GET, uri, "");
    StoragePoolDResponse poolResponse;
    Module::JsonHelper::JsonStringToStruct(resBody, poolResponse);
    if (poolResponse.m_error.code != 0) {
        // 根据设备反馈错误码，抛出
        ERRLOG("Fail to QueryStorageArray, ip: %s,error code: %d, error message: %s", m_ip.c_str(),
            poolResponse.m_error.code, poolResponse.m_error.description.c_str());
        ret = poolResponse.m_error.code;
    } else {
        INFOLOG("QueryStorageArray, ip: %s, pool id: %s, pool name: %s, storage id: %s", m_ip.c_str(),
            poolId.c_str(), poolResponse.m_storagePoolDatas.m_name.c_str(),
            poolResponse.m_storagePoolDatas.m_id.c_str());
        poolData = poolResponse.m_storagePoolDatas;
    }
    return ret;
}

std::string StorageClient::GetStorageSnId()
{
    return storageDeviceAuthData.deviceId;
}

int32_t StorageClient::DeleteSnapshot(const std::string &snapshotId)
{
    std::string uri = "/deviceManager/rest/" + storageDeviceAuthData.deviceId + "/snapshot/" + snapshotId;
    auto[ret, resBody] = SendRequest(METHOD_DELETE, uri, "");
    StorageCommonResponse storageCommonResponse;
    Module::JsonHelper::JsonStringToStruct(resBody, storageCommonResponse);
    if (storageCommonResponse.m_error.code != 0) {
        // 根据设备反馈错误码，抛出
        ERRLOG("Fail to delete snapshot, ip: %s, error code: %d, error message: %s",
            m_ip.c_str(), storageCommonResponse.m_error.code, storageCommonResponse.m_error.description.c_str());
        ret = storageCommonResponse.m_error.code;
    }
    return ret;
}

int32_t StorageClient::ActivateSnapshot(const std::vector<std::string> &snapshotIds)
{
    SnapshotBatchActParam snapshotBatchActParam(snapshotIds);
    std::string reqBody;
    Module::JsonHelper::StructToJsonString(snapshotBatchActParam, reqBody);

    std::string uri = "/deviceManager/rest/" + storageDeviceAuthData.deviceId + "/snapshot/activate";
    auto[ret, resBody] = SendRequest(METHOD_POST, uri, reqBody);
    StorageCommonResponse storageCommonResponse;
    Module::JsonHelper::JsonStringToStruct(resBody, storageCommonResponse);
    if (storageCommonResponse.m_error.code != 0) {
        // 根据设备反馈错误码，抛出
        ERRLOG("Fail to stop snapshot, ip: %s,error code: %d, error message: %s", m_ip.c_str(),
            storageCommonResponse.m_error.code, storageCommonResponse.m_error.description.c_str());
        ret = storageCommonResponse.m_error.code;
    }
    return ret;
}

void StorageClient::InitHttpRequest(HttpRequest &req)
{
    req.method = "";
    req.url = "";
    req.certCA = "";
    req.clientCert = "";
    req.clientKey = "";
    req.tmpUrl = "";
    req.vmPathInfo1 = "";
    req.vmPathInfo0 = "";
    req.cert = "";
    req.heads.clear();
    req.body = "";
    req.specialNetworkCard = "";
    req.isVerify = Module::CACertVerification::DO_NOT_VERIFY;
    req.localFilePath = "";
    req.domainInfo = "";
    req.vSphereLoginInfo = "";
}

int32_t StorageClient::Send(const HttpRequest &req, HttpResponseInfo &httpResponse, bool isRetry)
{
    int32_t iRet = FAILED;
    IHttpClient *httpClient = IHttpClient::GetInstance();
    if (httpClient == nullptr) {
        ERRLOG("Initialize httpClient failed.");
        return iRet;
    }

    std::shared_ptr<IHttpResponse> dpaHttpRespone = nullptr;
    uint32_t retryTimes = 0;
    while (retryTimes < SEND_HTTP_MAX_RETRY_TIMES) {
        uint32_t connectTimeOut = Module::ConfigReader::getUint("General", "ConnectTimeOut");
        uint32_t totalTimeOut = Module::ConfigReader::getUint("General", "TotalTimeOut");
        dpaHttpRespone = httpClient->SendMemCertRequest(req, connectTimeOut, totalTimeOut);
        if (dpaHttpRespone == nullptr) {
            WARNLOG("DpaHttpRespone is null. retry times: %d", retryTimes);
            retryTimes++;
            continue;
        }
        int32_t errCode = dpaHttpRespone->GetErrCode();
        httpResponse.m_statusCode = dpaHttpRespone->GetHttpStatusCode();
        if (dpaHttpRespone->Success()) {
            httpResponse.m_body = dpaHttpRespone->GetBody();
            httpResponse.cookies = dpaHttpRespone->GetCookies();
            iRet = SUCCESS;
            break;
        } else if (!IsNetworkError(httpResponse.m_statusCode)) {
            // 如果因为非网络原因失败，不用再重试
            httpResponse.m_body = dpaHttpRespone->GetBody();
            iRet = SUCCESS;
            break;
        }
        ERRLOG("Send request failed, url:%s:,error code: %d, response status code: %d", req.url.c_str(),
               errCode, dpaHttpRespone->GetHttpStatusCode());
        retryTimes++;
        sleep(SEND_HTTP_DELAY_TIME);
        if (!isRetry) {
            break;
        }
    }
    IHttpClient::ReleaseInstance(httpClient);
    return iRet;
}

std::pair <int32_t, std::string> StorageClient::SendRequest(const std::string &method, const std::string &uri,
                                                            const std::string &body)
{
    HttpRequest httpRequest;
    InitHttpRequest(httpRequest);
    httpRequest.method = method;
    httpRequest.url = "https://" + m_ip + ":" + std::to_string(m_port) + uri;
    httpRequest.body = body;
    httpRequest.isVerify = Module::CACertVerification::DO_NOT_VERIFY;
    httpRequest.heads.insert(std::make_pair("iBaseToken", storageDeviceAuthData.iBaseToken));
    httpRequest.heads.insert(std::make_pair("Cookie", *(storageDeviceAuthData.cookies.begin())));

    HttpResponseInfo httpResponse;
    int ret = Send(httpRequest, httpResponse);
    StorageCommonResponse storageCommonResponse;
    Module::JsonHelper::JsonStringToStruct(httpResponse.m_body, storageCommonResponse);
    INFOLOG("SendRequest ip : %s, code: %d", m_ip.c_str(), storageCommonResponse.m_error.code);
    if (IsNoAuthToAccess(storageCommonResponse.m_error.code) || IsIpError(storageCommonResponse.m_error.code)) {
        int32_t reLoginRet = ReLogin();
        if (reLoginRet != SUCCESS) {
            ERRLOG("ReLogin failed");
        } else {
            httpRequest.url = "https://" + m_ip + ":" + std::to_string(m_port) + uri;
            httpRequest.heads.clear();
            httpRequest.heads.insert(std::make_pair("iBaseToken", storageDeviceAuthData.iBaseToken));
            httpRequest.heads.insert(std::make_pair("Cookie", *(storageDeviceAuthData.cookies.begin())));
            INFOLOG("ReLogin send request");
            ret = Send(httpRequest, httpResponse);
        }
    }
    if (ret != SUCCESS) {
        return std::make_pair(STORAGE_AUTH_FAILED, "{}");
    }
    return std::make_pair(ret, httpResponse.m_body);
}

int32_t StorageClient::ReLogin()
{
    std::lock_guard<std::mutex> lock(ReCheckAccessAuthMutex);
    // 重新登录
    for (const std::string authIp : m_ipList) {
        auto [ret, storageDeviceAuthResponse] = CheckAccessAuthentication(authIp, m_port, accessAuthParam);
        if (storageDeviceAuthResponse.error.code != 0) {
            // 根据设备反馈错误码，抛出
            ret = storageDeviceAuthResponse.error.code;
            ERRLOG("Storage device authentication failed, ip:%s, port:%d, username:%s, error code:%d",
                authIp.c_str(), m_port, accessAuthParam.m_userName.c_str(), ret);
        } else {
            // 更新当前客户端的cookies和iBaseToken信息
            m_ip = authIp;
            this->storageDeviceAuthData = storageDeviceAuthResponse.storageDeviceAuthData;
            return SUCCESS;
        }
    }
    return FAILED;
}


bool StorageClient::IsNetworkError(const uint32_t statusCode)
{
    return std::count(NETWORK_HTTP_STATUS.begin(), NETWORK_HTTP_STATUS.end(), statusCode);
}

bool StorageClient::IsIpError(const uint32_t statusCode)
{
    return std::count(WRONG_IP_STATUS.begin(), WRONG_IP_STATUS.end(), statusCode);
}

bool StorageClient::IsNoAuthToAccess(const uint32_t statusCode)
{
    return std::count(NO_AUTH_HTTP_STATUS.begin(), NO_AUTH_HTTP_STATUS.end(), statusCode);
}
}